package com.hx.service.Impl;

import com.hx.mapper.OrderMapper;
import com.hx.mapper.StockMapper;
import com.hx.mapper.UserMapper;
import com.hx.pojo.Orders;
import com.hx.pojo.Stocks;
import com.hx.pojo.Users;
import com.hx.service.OrderService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 使用悲观锁秒杀
     * @param id
     * @return
     */
    @Override
    public Integer kill0(Integer id) {

        //查看是否还是库存
        Stocks stock = checkHasStock(id);
        //更新库存
        updateNewStock(stock);
        //生成秒杀订单
        Integer orderId = createKillOrder(stock);

        return orderId;
    }


    /**
     * 使用乐观锁秒杀
     * @param id
     * @return
     */
    @Override
    public Integer kill1(Integer id) {

        //查看是否还是库存
        Stocks stock = checkHasStock(id);
        //更新库存
        int updateRows = stockMapper.updateSaleOfStock(stock);
        if (updateRows == 0){
            log.error("抢购时，已售商品数据量更新失败！");
            throw new RuntimeException("抢购失败，请重新尝试！");
        }
        //生成秒杀订单
        Integer orderId = createKillOrder(stock);

        return orderId;
    }

    /**
     * 使用乐观锁+redis设置时间的秒杀活动
     * @param id
     * @return
     */
    @Override
    public Integer kill3(Integer id) {

        if (!redisTemplate.hasKey("kill"+id)){
            log.error("当前秒杀活动{}已关闭或不存在！","kill"+id);
            throw new RuntimeException("当前秒杀活动未开放！");
        }

        //查看是否还是库存
        Stocks stock = checkHasStock(id);
        //更新库存
        int updateRows = stockMapper.updateSaleOfStock(stock);
        if (updateRows == 0){
            log.error("抢购时，已售商品数据量更新失败！");
            throw new RuntimeException("抢购失败，请重新尝试！");
        }
        //生成秒杀订单
        Integer orderId = createKillOrder(stock);

        return orderId;
    }

    /**
     * 根据商品id和用户id生成唯一MD5码，防止脚本爬虫。
     * @param stockId
     * @param userId
     * @return
     */
    @Override
    public String getMd5(Integer stockId, Integer userId) {

        Users user = userMapper.findUserById(userId);
        if (user == null){
            log.error("id为{}的用户不存在！",userId);
            throw new RuntimeException("当前用户不存在！");
        }

        Stocks stock = stockMapper.checkStock(stockId);
        if (stock == null){
            log.error("id为{}的商品不存在！",stockId);
            throw new RuntimeException("当前商品不存在！");
        }

        //设置md5和随机撒盐
        String md5Key = userId + "_kill_" + stockId;
        String md5Value = DigestUtils.md5DigestAsHex((md5Key+"何祥好帅").getBytes(StandardCharsets.UTF_8));

        //保存在redis,并设置过期时间
        redisTemplate.opsForValue().set(md5Key,md5Value,360, TimeUnit.SECONDS);
        log.info("Redis写入:key{},value{}",md5Key,md5Value);
        return md5Value;

    }

    /**
     * 携带md5秒杀
     * @param stockId
     * @param userId
     * @param md5Value
     * @return
     */
    @Override
    public Integer kill5(Integer stockId, Integer userId, String md5Value) {


        if (!redisTemplate.hasKey("kill"+stockId)){
            log.error("当前秒杀活动{}已关闭或不存在！","kill"+stockId);
            throw new RuntimeException("当前秒杀活动未开放！");
        }

        String myMD5 = userId + "_kill_" + stockId;
        String myMD5Value = (String)redisTemplate.opsForValue().get(myMD5);
        if (myMD5Value == null || !myMD5Value.equals(md5Value)){
            log.error("非法进行秒杀！");
            throw new RuntimeException("非法进行秒杀！");
        }

        //查看是否还是库存
        Stocks stock = checkHasStock(stockId);
        //更新库存
        int updateRows = stockMapper.updateSaleOfStock(stock);
        if (updateRows == 0){
            log.error("抢购时，已售商品数据量更新失败！");
            throw new RuntimeException("抢购失败，请重新尝试！");
        }
        //生成秒杀订单
        Integer orderId = createKillOrder(stock);

        return orderId;

    }

    /**
     * 根据商品信息生成抢购订单
     * @param stock
     * @return
     */
    private Integer createKillOrder(Stocks stock) {

        Orders order = new Orders();
        order.setSid(stock.getId());
        order.setName(stock.getName());
        order.setCreateTime(new Date());

        int orderRows = orderMapper.createOrder(order);
        if (orderRows == 0){
            return 0;
        }
        return order.getId();
    }

    /**
     * 更新库存信息
     * @param stock
     */
    private void updateNewStock(Stocks stock) {

        stock.setSale(stock.getSale() + 1);
        int updateRows = stockMapper.updateStock(stock);
        if (updateRows == 0){
            log.error("抢购时，已售商品数据量更新失败！");
            throw new RuntimeException("抢购失败，请重新尝试！");
        }

    }

    /**
     * 查看当前商品id是否有库存
     * @param id
     * @return
     */
    private Stocks checkHasStock(Integer id) {

        Stocks stock = stockMapper.checkStock(id);

        if (stock == null){
            log.error("未查询到有关商品信息");
            throw new RuntimeException("校验商品信息失败！");
        }
        if (stock.getCount().equals(stock.getSale())){
            log.error("商品{},库存:{}，已售:{}",id,stock.getCount(),stock.getSale());
            throw new RuntimeException("库存不足！");
        }
        return stock;
    }
}
